/*
 * This is my server program which will run what my client program tells it to
 * when the client has logged in.
 *
 * This uses TCP/IP.
 *
 * This was adapted from Claypool's listen-tcp.c code.
 * myFork() function was made with some helpful code from:
 *	www.amparo.net/ce155/forkex.html
 * 
 * By: Jonathan Marokhovsky
 * Version: November 17, 2011
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/wait.h>
#include <errno.h>

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff /* should be in <netinet/in.h> */
#endif

#define MESSAGE_MAX 1024
#define PW_MAX		80
#define PASSWORD	"password"
#define ACCEPTED	"ok"
#define CMD_MAX		80

int getSocket(void);
void exitSocket(int sock, int exitNum);
int stdBind(int port, int socket);
int dup(int oldfd);
int dup2(int oldfd, int newfd);
int myFork(int socket/*might not need this*/);

int debug;

int main(int argc, char *argv[])
{
	char theMessage[MESSAGE_MAX];
	char *message;
	char *cmdArgs[CMD_MAX];
	char *cmd;
	char sentPW[PW_MAX];
	int bytes, cmdCount, i;
	int pwFlag = 0;
	int pwLen = (int) strlen(PASSWORD);
	int sock, port, clientLen, newSocket;
	const char delims[] = " "; // going to parse the incoming string by space
	struct addrinfo addressInfo;
	struct sockaddr_in clientAddr;
	pid_t pID;
	
	debug = 1; // for now assume that I want all debug messages
	message = theMessage;
	
	if (argc != 2)
	{
		fprintf(stderr, "%s - server to accept TCP connections\n", argv[0]);
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		fprintf(stderr, "\t<port>\t- port to listen on\n");
		exit(1);
	}
	
	port = atoi(argv[1]);

	printf("Activating Server...\n");
	printf("Trying to create socket at port %d...\n", port);
	
	/* Create the socket */
	sock = getSocket();
	
	/* bind to that socket */
	stdBind(port, sock);
	
	
	while (1)
	{
		/* listen on that socket with a listen queue of 5 */
		listen(sock, 5);
	
		/* when someone tries to connect: */
		clientLen = sizeof(clientAddr);
		printf("Waiting for client...\n");
		newSocket = accept(sock, (struct sockaddr *) &clientAddr, &clientLen);
		if (newSocket < 0)
		{
			perror("accepting connection");
			exit(4);
		}
		
		printf("Connected to socket %d!\n", newSocket);
		pID = fork(); // once connected, fork()
		if (pID >= 0) // successful fork
		{
			if (pID == 0) // child process
			{
				printf("CLIENT: Created a child process\n");
				printf("CLIENT: Sending okay to the socket\n");
				
				/* receive the password the client sent */
				if ((bytes = read(newSocket, message, MESSAGE_MAX)) > 0)
				{
					message[bytes] = '\0';
					printf("CLIENT: The recieved password is: '%s'\n", message);
					if (strncmp(PASSWORD, message, strlen(PASSWORD)) == 0) // tell the client they are logged in
					{
						if (write(newSocket, ACCEPTED, (int) strlen(ACCEPTED)) == -1)
						{
							perror("handshake failed");
							exitSocket(newSocket, 7);
						}
						
						printf("Receiving the command\n");
						
						if ((bytes = read(newSocket, message, MESSAGE_MAX)) > 0)
						{
							printf("What was received: '%s'\n", message);
							cmd = strtok(message, delims);
							i = 0;
							
							while (cmd != NULL)
							{
								printf("%s\n", cmd);
								cmdArgs[i] = cmd;
								cmd = strtok(NULL, delims);
								i++;
								if (i > 80)
									break;
							}
							cmdArgs[i] = (char *) 0;
							printf("Calling Exec\n");
							/* redirect stderr and stdout to the client */
							/* redirect stdout */
							printf("Attempting to dup stdout to %d\n", newSocket);
							fflush(stdout);
							if (dup2(newSocket, 1) < 0)
							{
								perror("error piping stdout");
								exit(5);
							}
							/* redirect stderr */
							// printf("Attempting to dup stderr to %d\n", newSocket);
							fflush(stderr);
							if (dup2(newSocket, 2) < 0)
							{
								perror("error piping stderr");
								exit(6);
							}
							execvp(cmdArgs[0], cmdArgs);
						}
					}
					else
					{
						if (write(newSocket, "Incorrect Password", (int) strlen("Incorrect Password")))
						{
							perror("sending pw fail");
							exitSocket(newSocket, 8);
						}
					}
				}
				printf("CHILD: Exiting...\n");
				exit(0); // successfully exit
			}			
		}
		else
		{
			perror("error forking");
			exitSocket(newSocket, 6);
		}
		close(newSocket);
	}// end of client interaction
	close(sock);
	
	return 0;
}

/*
 * This function just standardizes how a socket it acquired and adds some debug
 * messages.  It also has a killswitch incase socket() returns -1.
 */
int getSocket(void)
{
	int ret;
	if (debug) printf("Getting a socket\n");
	ret = socket(/*AF_LOCAL*/PF_INET, SOCK_STREAM, 0); // AF_LOCAL didn't work so made it PF_INET
	if (debug) printf("\t-socket() returned: %d\n", ret);
	if (ret < 0)
	{
		perror("Failed to get a socket!\n");
		exit(2);
	}
	return ret;
}

/*
 * This standardizes creating a server port
 */
int stdBind(int port, int socket)
{
	if (debug) printf("Binding the local address to: \n");
	if (debug) printf("\tSocket: %d\n\tPort: %d\n", socket, port);
	struct sockaddr_in addr;
	int bindOut;
	bzero((char *) &addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(port);
	bindOut = bind(socket, (struct sockaddr *) &addr, sizeof(addr));
	if (bindOut < 0)
	{
		perror("can't bind to local address");
		exit(3);
	}
	printf("Created the socket! Accepting connections at:\n");
	printf("Socket:\t%d\nPort:\t%d\n", socket, port);
	return 0;
}

/*
 * This does the given exit as well as closing the given socket.
 * This should be used after a socket has been opened (just to make sure the
 * socket gets closed).
 */
void exitSocket(int sock, int exitNum)
{
	close(sock);
	exit(exitNum);
}


